"""
SaaS接口请求工具类 - Python版本
基于Java版本的SaasHttpUtil转换而来

@author 周士钰
@date 2025/06/06
"""

import orjson
import uuid
import requests
from typing import Dict, Optional, Union
import logging
from config.config_manager import get_saas_config

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class SaasConfig:
    """SaaS配置类 - 从配置文件读取配置"""
    def __init__(self):
        # 从配置文件获取SaaS配置
        saas_config = get_saas_config()
        self.base_url = saas_config.get("base_url")
        self.timeout = saas_config.get("timeout")
        # 已禁用重试机制，以下配置项仅保留占位，不生效
        self.max_retry_times = 0
        self.retry_delay = 0.0
        self.enable_retry = False


class SaasException(Exception):
    """SaaS HTTP异常类"""
    def __init__(self, message: str, endpoint: str = None, request_body: str = None, 
                 response_body: str = None, http_status: int = None, original_exception: Exception = None):
        super().__init__(message)
        self.endpoint = endpoint
        self.request_body = request_body
        self.response_body = response_body
        self.http_status = http_status
        self.original_exception = original_exception


def _create_auth_headers(token: str) -> Dict[str, str]:
    # 创建认证请求头
    return {
        "Authorization": token,
        "Access-Token": token,
        "Content-Type": "application/json"
    }


class SaasUtil:
    """SaaS接口请求工具类"""
    
    # 接口端点常量
    ACCESS_TOKEN = "/drug/interface/medins/accessToken"
    UPLOAD_HIS_DRUG_INFO = "/system/interface/medins/importDrug"

    GET_INFO = "/system/user/profile"
    GET_SAAS_DRUG_INFO = "/setl/interface/medins/druglist"
    GET_SHIPPING_INFO = "/purc/interface/medins/getShippingInformation"
    GET_DRUG_ID_CODE = "/system/interface/medins/getDrugIdCode"
    UPLOAD_PRESCRIPTION_TRACKING_LOG_LIST = "/purc/prescriptionTrackingLog/uploadPrescriptionTrackingLogList"
    QUERY_TRAC_DRUG = "/system/interface/pda/queryTracDrug"
    QUERY_TRAC_DRUG_YZ = "/system/interface/pda/queryTracDrugYz"
    GET_TRAC_CODG_STORE = "/system/interface/pda/getTracCodgStore"
    CONFIRM_DISP_DRUG = "/system/interface/pda/confirmDispDrug"
    UPLOAD_HIS_ENTERPRISE_INFO = "/system/interface/medins/importEntp"
    RETN_DRUG = "/system/interface/pda/retnDrug"
    
    def __init__(self, config: SaasConfig = None):
        self.config = config or SaasConfig()
        self._session = requests.Session()

     # ============== 日志脱敏与安全工具 ==============
    def _mask_headers(self, headers: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]:
        if not headers:
            return headers
        masked = dict(headers)
        for key in ["Authorization", "Access-Token", "access-token", "authorization"]:
            if key in masked and masked[key]:
                token = masked[key]
                if len(token) > 12:
                    masked[key] = f"{token[:6]}...{token[-4:]}"
                else:
                    masked[key] = "***"
        return masked

    def _mask_body(self, body: Union[dict, list, None]):
        if body is None:
            return None
        if isinstance(body, list):
            return [self._mask_body(item) for item in body]
        if isinstance(body, dict):
            masked = {}
            for k, v in body.items():
                key_lower = str(k).lower()
                if key_lower in {"password", "userpassword", "user_password", "userpasswd", "pwd"}:
                    masked[k] = "***"
                else:
                    masked[k] = self._mask_body(v) if isinstance(v, (dict, list)) else v
            return masked
        return body

    def _execute_post_request(self, endpoint: str, request_body: dict = None,
                            headers: Dict[str, str] = None, log_prefix: str = "") -> dict:
        """简化的POST请求方法，直接处理dict→json→dict"""
        url = self.config.base_url + endpoint
        
        logger.info(f"{log_prefix} 请求地址：{url} post")
        
        if headers:
            logger.info(f"{log_prefix} 请求头：{orjson.dumps(self._mask_headers(headers)).decode('utf-8')}")
        
        if request_body is not None:
            logger.info(f"{log_prefix} 请求数据：{orjson.dumps(self._mask_body(request_body)).decode('utf-8')}")
        
        try:
            response = self._session.post(
                url,
                json=request_body,
                headers=headers,
                timeout=self.config.timeout
            )
            
            logger.info(f"{log_prefix} 响应数据：{response.text}")
            
            # 检查HTTP状态码
            if not (200 <= response.status_code < 300):
                error_msg = f"{log_prefix} HTTP请求失败，状态码: {response.status_code}"
                
                # 特殊处理502错误
                if response.status_code == 502:
                    error_msg += " (服务器网关错误，可能是后端服务未启动或配置问题)"
                elif response.status_code == 404:
                    error_msg += " (接口不存在，请检查接口路径)"
                elif response.status_code == 401:
                    error_msg += " (认证失败，请检查用户名密码)"
                elif response.status_code == 403:
                    error_msg += " (权限不足，请检查用户权限)"
                
                raise SaasException(
                    error_msg,
                    endpoint=endpoint,
                    request_body=(orjson.dumps(request_body).decode('utf-8') if request_body is not None else None),
                    response_body=response.text,
                    http_status=response.status_code
                )
            
            # 直接返回解析后的JSON对象
            try:
                return response.json()
            except ValueError:
                return orjson.loads(response.text)
            
        except requests.exceptions.ConnectionError as e:
            logger.error(f"{log_prefix} 网络连接失败：{str(e)}")
            raise SaasException(
                f"{log_prefix} 网络连接失败，请检查服务器地址和网络连接: {str(e)}",
                endpoint=endpoint,
                request_body=(orjson.dumps(request_body).decode('utf-8') if request_body is not None else None),
                original_exception=e
            )
        except requests.exceptions.Timeout as e:
            logger.error(f"{log_prefix} 请求超时：{str(e)}")
            raise SaasException(
                f"{log_prefix} 请求超时，请检查网络连接或增加超时时间: {str(e)}",
                endpoint=endpoint,
                request_body=(orjson.dumps(request_body).decode('utf-8') if request_body is not None else None),
                original_exception=e
            )
        except requests.exceptions.RequestException as e:
            logger.error(f"{log_prefix} 请求异常：{str(e)}")
            raise SaasException(
                f"{log_prefix} 请求失败: {str(e)}",
                endpoint=endpoint,
                request_body=(orjson.dumps(request_body).decode('utf-8') if request_body is not None else None),
                original_exception=e
            )
    
    def _execute_get_request(self, endpoint: str, headers: Dict[str, str] = None, 
                           log_prefix: str = "") -> dict:
        """简化的GET请求方法，直接处理json→dict"""
        url = self.config.base_url + endpoint
        
        logger.info(f"{log_prefix} 请求地址：{url} get")
        
        if headers:
            logger.info(f"{log_prefix} 请求头：{orjson.dumps(self._mask_headers(headers)).decode('utf-8')}")
        
        try:
            response = self._session.get(
                url,
                headers=headers,
                timeout=self.config.timeout
            )
            
            logger.info(f"{log_prefix} 响应数据：{response.text}")
            
            # 检查HTTP状态码
            if not (200 <= response.status_code < 300):
                raise SaasException(
                    f"{log_prefix} HTTP请求失败，状态码: {response.status_code}",
                    endpoint=endpoint,
                    response_body=response.text,
                    http_status=response.status_code
                )
            # 直接返回解析后的JSON对象
            try:
                return response.json()
            except ValueError:
                return orjson.loads(response.text)
            
        except requests.exceptions.RequestException as e:
            logger.error(f"{log_prefix} 请求异常：{str(e)}")
            raise SaasException(
                f"{log_prefix} 请求失败: {str(e)}",
                endpoint=endpoint,
                original_exception=e
            )
    
    def _add_common_fields(self, request_data: dict) -> dict:
        # 创建请求数据的副本，避免修改原始数据
        enhanced_data = request_data.copy()
        # 自动生成requestID（如果不存在）
        if "requestID" not in enhanced_data:
            enhanced_data["requestID"] = str(uuid.uuid4()).replace("-", "")
        return enhanced_data
    

    def _handle_saas_response(self, result: Optional[dict], log_prefix: str, request_data: dict = None) -> bool:
        """
        通用的SaaS响应处理器
        :param result: SaaS接口返回的响应字典
        :param log_prefix: 日志前缀，用于区分不同接口的日志
        :param request_data: 原始请求数据，用于错误日志记录
        :return: True表示业务成功，False表示业务失败
        """
        if not result or result.get("returnCode") != 0:
            logger.error(f"{log_prefix} 业务异常")
            # 使用脱敏方法记录请求和响应
            masked_request = self._mask_body(request_data) if request_data else "N/A"
            logger.error(f"{log_prefix} 请求数据: {orjson.dumps(masked_request).decode('utf-8')}")
            logger.error(f"{log_prefix} 响应数据: {orjson.dumps(result).decode('utf-8')}")
            return False
        
        logger.info(f"{log_prefix} 成功")
        return True

    def get_access_token(self,user_account:str,user_password:str):
        request_data = {
            "userAccount": user_account,
            "userPassWord":user_password
        }
        log_prefix = "getAccessToken"
        result = self._execute_post_request(self.ACCESS_TOKEN,request_data,None,log_prefix)

        if self._handle_saas_response(result, log_prefix, request_data):
            return result.get("authorization")
        return None

    def get_info(self, token: str):
        """获取用户信息 - 简化版本，直接返回JSON对象"""
        log_prefix = "getInfo"
        headers = _create_auth_headers(token)
        return self._execute_get_request(self.GET_INFO, headers, log_prefix)

    def upload_his_drug_info(self, token: str,request_data: dict) -> Optional[dict]:
        """上传HIS药品字典"""
        log_prefix = "上传his药品字典"
        headers = _create_auth_headers(token)
        request_data_full = self._add_common_fields(request_data)
        result = self._execute_post_request(
            self.UPLOAD_HIS_DRUG_INFO,
            request_data_full,
            headers,
            log_prefix
        )

        if self._handle_saas_response(result, log_prefix, request_data_full):
            return result  # 成功时返回完整的响应字典
        return None  # 失败时返回None

    def get_shipping_information(self, token: str, request_data: dict) -> Optional[dict]:
        """获取收货信息"""
        log_prefix = "获取收货信息"
        headers = _create_auth_headers(token)
        request_data_full = self._add_common_fields(request_data)
        result = self._execute_post_request(
            self.GET_SHIPPING_INFO,
            request_data_full,
            headers,
            log_prefix
        )

        if self._handle_saas_response(result, log_prefix, request_data_full):
            return result  # 成功时返回完整的响应字典
        return None  # 失败时返回None